import * as vscode from "vscode";
import * as path from "path";
import * as fs from "fs";
import { VsCodeUtil } from "@/utils/VsCodeUtil";

import {
  GLOBAL_DORADO_IDE_EXTENSION_ID,
  GLOBAL_DORADO_IDE_WEB_VIEW_ID,
} from "@/config";

// 定义消息类型接口
interface WebViewMessage {
  cmd: string;
  data: {
    filePathName?: string; //文件路径名称
    fileContent?: string; //文件内容
    [key: string]: unknown;
  };
}

/**
 * 资源路径常量
 * WEBVIEW_BASE资源路径根目录：/resources/webview/
 */
const RESOURCES = {
  WEBVIEW_BASE: ["resources", "webview"],
  ICONS: {
    LIGHT: "resources/dark/UI-editor-light.svg",
    DARK: "resources/dark/UI-editor-dark.svg",
  },
  WEBVIEW: {
    HTML: "ide.html",
    JS: {
      CHUNK_VENDORS: "js/chunk-vendors.js",
      MY_FILE: "js/ide.js",
    },
    CSS: "assets/styles.css",
  },
} as const;

/**
 * @Description :  dorado视图编辑器类
 * @Date : 2025/2/5 16:08:08
 * @author : hans
 */
export class DoradoViewEditor {
  private readonly context: vscode.ExtensionContext;
  private readonly extensionId: string;
  private readonly webViewId: string;
  private webViewPanels: Map<string, vscode.WebviewPanel>;

  constructor(context: vscode.ExtensionContext) {
    this.extensionId = GLOBAL_DORADO_IDE_EXTENSION_ID;
    this.webViewId = GLOBAL_DORADO_IDE_WEB_VIEW_ID;
    this.context = context;
    this.webViewPanels = new Map();
  }

  /**
   * @Description :  ide初始化
   * @Date : 2025/2/5 16:08:08
   * @author : hans
   * @param uri 选中的文件路径
   * @param maps 已存在的WebView面板Map
   */
  public async init(
    uri: vscode.Uri,
    maps: Map<string, vscode.WebviewPanel>
  ): Promise<void> {
    try {
      const fileName = path.basename(uri.fsPath);
      const filePath = uri.fsPath;

      this.webViewPanels = maps;
      const content = await this.getFileContent(uri);
      await this.openWebViewPanel(filePath, fileName, content);
      // !!showInformationMessage 不支持指定显示时间，只能用状态栏消息进行提醒到用户
      VsCodeUtil.showStatusMessage(
        `打开Dorado视图文件: ${filePath}`,
        3000,
        undefined,
        "文件已在编辑器中打开"
      );
    } catch (error) {
      this.handleError(error, "初始化Dorado IDE失败");
    }
  }

  /**
   * 在工作区打开一个Webview面板
   * @param filePath 文件路径
   * @param panelName 视图名称
   * @param content 文件内容
   */
  private async openWebViewPanel(
    filePath: string,
    panelName: string,
    content?: string
  ): Promise<void> {
    if (!panelName) {
      return;
    }

    const panelKey = filePath + panelName;
    if (this.webViewPanels.has(panelKey)) {
      this.webViewPanels.get(panelKey)?.reveal(vscode.ViewColumn.One);
      return;
    }

    const newPanel = await this.createWebViewPanel(
      filePath,
      panelName,
      content
    );
    this.webViewPanels.set(panelKey, newPanel);
    newPanel.onDidDispose(() => {
      this.webViewPanels.delete(panelKey);
    });
  }

  /**
   * 创建WebView面板
   */
  private async createWebViewPanel(
    filePath: string, //打开的文件绝对路径
    panelName: string, //打开的文件名称
    content?: string //打开的文件内容
  ): Promise<vscode.WebviewPanel> {
    const panel = vscode.window.createWebviewPanel(
      this.webViewId,
      panelName || "未命名.view.xml",
      vscode.ViewColumn.One,
      {
        enableScripts: true,
        retainContextWhenHidden: true,
      }
    );
    // 设置WebView内容
    await this.setupWebViewContent(panel, filePath, content);
    // 设置WebView图标
    this.setupWebViewIcons(panel);
    // 设置WebView消息处理器
    this.setupWebViewMessageHandler(panel);

    return panel;
  }

  /**
   * 设置WebView内容
   */
  private async setupWebViewContent(
    panel: vscode.WebviewPanel,
    filePath: string,
    content?: string
  ): Promise<void> {
    const htmlContent = await this.getWebViewContent();
    const resourceUris = this.getResourceUris(panel);

    const escapedContent = content
      ? Buffer.from(content, "utf-8").toString("base64")
      : "";
    const escapedFilePathName = filePath
      ? Buffer.from(filePath, "utf-8").toString("base64")
      : "";

    panel.webview.html = this.replaceResourcePaths(
      htmlContent,
      resourceUris,
      escapedContent,
      escapedFilePathName
    );
  }

  /**
   * 获取资源URI
   */
  private getResourceUris(panel: vscode.WebviewPanel) {
    return {
      chunkVendors: panel.webview.asWebviewUri(
        vscode.Uri.joinPath(
          this.context.extensionUri,
          ...RESOURCES.WEBVIEW_BASE,
          ...RESOURCES.WEBVIEW.JS.CHUNK_VENDORS.split("/")
        )
      ),
      myJs: panel.webview.asWebviewUri(
        vscode.Uri.joinPath(
          this.context.extensionUri,
          ...RESOURCES.WEBVIEW_BASE,
          ...RESOURCES.WEBVIEW.JS.MY_FILE.split("/")
        )
      ),
      css: panel.webview.asWebviewUri(
        vscode.Uri.joinPath(
          this.context.extensionUri,
          ...RESOURCES.WEBVIEW_BASE,
          ...RESOURCES.WEBVIEW.CSS.split("/")
        )
      ),
    };
  }

  /**
   * 替换资源路径
   */
  private replaceResourcePaths(
    htmlContent: string,
    resourceUris: { [key: string]: vscode.Uri },
    escapedContent: string,
    escapedFilePathName: string
  ): string {
    return htmlContent
      .replace("${chunkVendorsUri}", resourceUris.chunkVendors.toString())
      .replace("${myJsUri}", resourceUris.myJs.toString())
      .replace("${cssUri}", resourceUris.css.toString())
      .replace("${escapedContent}", escapedContent)
      .replace("${filePathName}", escapedFilePathName);
  }

  /**
   * 设置WebView图标
   */
  private setupWebViewIcons(panel: vscode.WebviewPanel): void {
    panel.iconPath = {
      light: vscode.Uri.file(
        this.context.asAbsolutePath(RESOURCES.ICONS.LIGHT)
      ),
      dark: vscode.Uri.file(this.context.asAbsolutePath(RESOURCES.ICONS.DARK)),
    };
  }

  /**
   * 设置WebView消息处理器
   */
  private setupWebViewMessageHandler(panel: vscode.WebviewPanel): void {
    panel.webview.onDidReceiveMessage(
      async (message: WebViewMessage) => {
        console.log("Received message:", message);
        switch (message.cmd) {
          case "saveFile":
            await this.handleSaveFile(message.data);
            break;
          default:
            console.error("Unknown command:", message);
        }
      },
      undefined,
      this.context.subscriptions
    );
  }

  /**
   * 处理保存文件命令
   */
  private async handleSaveFile(data: WebViewMessage["data"]): Promise<void> {
    try {
      if (!data.filePathName || !data.fileContent) {
        throw new Error("文件路径或内容不能为空");
      }

      const filePathName = await this.decodeBase64(data.filePathName);
      const fileContent = await this.decodeBase64(data.fileContent);

      await this.saveFileContent(filePathName, fileContent);
      VsCodeUtil.showStatusMessage(`文件已成功保存: ${filePathName}`, 3000);
    } catch (error) {
      this.handleError(error, "保存文件时发生错误");
    }
  }

  /**
   * 读取文件内容
   * @param uri 文件路径
   * @returns 文件内容
   */
  private async getFileContent(uri: vscode.Uri): Promise<string> {
    try {
      const fileContent = await vscode.workspace.fs.readFile(uri);
      return Buffer.from(fileContent).toString("utf-8");
    } catch (error) {
      throw new Error(
        `读取文件失败: ${
          error instanceof Error ? error.message : String(error)
        }`
      );
    }
  }

  /**
   * 获取webview对应的ide.html内容（vue3+ts开发）
   * @returns ide.html内容
   */
  private async getWebViewContent(): Promise<string> {
    const htmlFilePath = path.join(
      this.context.extensionPath,
      ...RESOURCES.WEBVIEW_BASE,
      RESOURCES.WEBVIEW.HTML
    );
    return fs.promises.readFile(htmlFilePath, "utf8");
  }
  /**
   * 解码base64字符串
   * @param base64String base64字符串
   * @returns 解码后的字符串
   */
  private async decodeBase64(base64String: string): Promise<string> {
    return Buffer.from(base64String, "base64").toString("utf-8");
  }

  /**
   * 保存文件内容到本地磁盘
   * @param filePathName 文件路径
   * @param content 文件内容
   */
  private async saveFileContent(
    filePathName: string,
    content: string
  ): Promise<void> {
    const fileUri = vscode.Uri.file(filePathName);
    const encoder = new TextEncoder();
    const fileData = encoder.encode(content);
    await vscode.workspace.fs.writeFile(fileUri, fileData);
  }

  /**
   * 处理错误
   * @param error 错误
   * @param operation 操作
   */
  private handleError(error: unknown, operation: string): void {
    const errorMessage = error instanceof Error ? error.message : String(error);
    void vscode.window.showErrorMessage(`${operation}: ${errorMessage}`);
  }
}
